/*
 * Copyright (c) 2023, LXMS (lxinet.cn).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.lxinet.lxms.system.service;

import cn.lxinet.lxms.framework.core.exception.LxmsException;
import cn.lxinet.lxms.framework.core.helper.PageHelper;
import cn.lxinet.lxms.framework.core.utils.*;
import cn.lxinet.lxms.framework.core.vo.Page;
import cn.lxinet.lxms.framework.mybatis.service.SimpleService;
import cn.lxinet.lxms.framework.security.config.JwtConfig;
import cn.lxinet.lxms.framework.security.entity.LxmsUser;
import cn.lxinet.lxms.system.consts.SysConst;
import cn.lxinet.lxms.system.convert.SysUserConvert;
import cn.lxinet.lxms.system.dto.sysUser.SysLoginDto;
import cn.lxinet.lxms.system.dto.sysUser.SysUserCreateDto;
import cn.lxinet.lxms.system.dto.sysUser.SysUserUpdateDto;
import cn.lxinet.lxms.system.entity.SysUser;
import cn.lxinet.lxms.system.enums.common.SysStatus;
import cn.lxinet.lxms.system.enums.sysUser.LoginLogStatus;
import cn.lxinet.lxms.system.enums.sysUser.SysUserSex;
import cn.lxinet.lxms.system.mapper.SysUserMapper;
import cn.lxinet.lxms.system.message.SystemErrorCode;
import cn.lxinet.lxms.system.vo.SysRoleVo;
import cn.lxinet.lxms.system.vo.SysUserVo;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * 用户
 *
 * @author zcx
 * @date 2023/07/08
 */
@Slf4j
@Service
public class SysUserService extends SimpleService<SysUser> {
    @Autowired
    private SysUserMapper sysUserMapper;
    @Lazy
    @Autowired
    private JwtConfig jwtConfig;
    @Autowired
    private SysLoginLogService sysLoginLogService;
    @Autowired
    private SysUserPostService sysUserPostService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysPostService sysPostService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;
    @Autowired
    private SysDeptService sysDeptService;

    /**
     * 列表
     *
     * @param deptId  部门id
     * @param account 账号
     * @param name    姓名
     * @param email   邮箱
     * @param phone   手机号
     * @param sex     性别
     * @param status  状态
     * @return {@link Page}<{@link SysRoleVo}>
     */
    public List<SysUserVo> list(Long deptId, String account, String name, String email, String phone, Integer sex, Integer status){
        List<Long> deptIdList = new ArrayList<>();
        if (deptId > 0){
            deptIdList = sysDeptService.listSonIds(deptId);
        }
        return sysUserMapper.list(PageHelper.getPage(), deptIdList, account, name, email, phone, sex, status);
    }

    /**
     * 登录
     * @param sysLoginDto 登录dto
     * @return {@link String}
     */
    public SysUserVo login(SysLoginDto sysLoginDto, HttpServletRequest request){
        try {
            //优先account登录
            SysUser sysUser = findByName(sysLoginDto.name());
            Assert.notNull(sysUser, SystemErrorCode.NAME_OR_PASSWORD_INCORRECT);
            Assert.notNull(sysUser.getStatus().equals(SysStatus.ENABLE.value()), SystemErrorCode.ACCOUNT_IS_DISABLED);
            String decryptPwd = RSAUtil.getInstance().decrypt(sysLoginDto.password());
            Assert.isTrue(passwordEncoder.matches(decryptPwd, sysUser.getPassword()), SystemErrorCode.NAME_OR_PASSWORD_INCORRECT);
            boolean isAdministrator = sysUserRoleService.isAdministrator(sysUser.getId());
            String token = jwtConfig.genToken(new LxmsUser(sysUser.getId(), sysUser.getName(), isAdministrator, null));
            SysUserVo sysUserVo = SysUserConvert.INSTANCE.toVo(sysUser);
            sysUserVo.setToken(token);
            String ip = IpUtil.getIp(request);
            //登录成功修改信息
            sysUserMapper.loginSuccess(sysUser.getId(), ip);
            sysLoginLogService.add(sysLoginDto.name(), LoginLogStatus.SUCCESS);
            return sysUserVo;
        }catch (LxmsException e){
            sysLoginLogService.add(sysLoginDto.name(), LoginLogStatus.FAIL);
            throw e;
        }
    }

    public SysUserVo findUserVoById(Long id){
        SysUser sysUser = findById(id);
        SysUserVo sysUserVo = SysUserConvert.INSTANCE.toVo(sysUser);
        List<Long> postIds = sysUserPostService.listPostIdsByUserId(id);
        List<Long> roleIds = sysUserRoleService.listRoleIdsByUserId(id);
        sysUserVo.setPostIdList(postIds);
        sysUserVo.setRoleIdList(roleIds);
        return sysUserVo;
    }

    /**
     * 添加
     * @param dto dto
     * @param ip ip
     */
    @Transactional(rollbackFor = Exception.class)
    public void add(SysUserCreateDto dto, String ip){
        SysUser accountUser = sysUserMapper.findByName(dto.getName());
        Assert.isNull(accountUser, SystemErrorCode.USER_ACCOUNT_EXIST);
        if (StringUtils.isNotBlank(dto.getPhone())){
            SysUser phoneUser = sysUserMapper.findByPhone(dto.getPhone());
            Assert.isNull(phoneUser, SystemErrorCode.USER_PHONE_EXIST);
        }
        if (StringUtils.isNotBlank(dto.getEmail())){
            SysUser emailUser = sysUserMapper.findByEmail(dto.getEmail());
            Assert.isNull(emailUser, SystemErrorCode.USER_EMAIL_EXIST);
        }
        SysUser sysUser = SysUserConvert.INSTANCE.toEntity(dto);
        sysUser.setSecret(RandomCodeUtil.uuid());
        String decryptPwd = RSAUtil.getInstance().decrypt(sysUser.getPassword());
        sysUser.setPassword(passwordEncoder.encode(decryptPwd));
        if (sysUser.getSex().equals(SysUserSex.UNKNOWN.value())){
            sysUser.setAvatar(SysConst.DEFAULT_AVATAR.UNKNOW);
        }else if (sysUser.getSex().equals(SysUserSex.MAN.value())){
            sysUser.setAvatar(SysConst.DEFAULT_AVATAR.MAN);
        }else if (sysUser.getSex().equals(SysUserSex.WOMAN.value())){
            sysUser.setAvatar(SysConst.DEFAULT_AVATAR.WOMAN);
        }
        sysUser.setCreateIp(ip);
        super.insert(sysUser);
        //用户和岗位关联
        sysUserPostService.batchSave(sysUser.getId(), dto.getPostIdList());
        //用户和角色关联
        sysUserRoleService.batchSave(sysUser.getId(), dto.getRoleIdList());
    }

    /**
     * 修改
     * @param dto dto
     * @return {@link Integer}
     */
    public void update(SysUserUpdateDto dto){
        SysUser dbSysUser = findById(dto.getId());
        Assert.notNull(dbSysUser, SystemErrorCode.USER_NOT_EXIST);
        if (StringUtils.isNotBlank(dto.getPhone()) && !dbSysUser.getPhone().equals(dto.getPhone())){
            SysUser phoneUser = sysUserMapper.findByPhone(dto.getPhone());
            Assert.isNull(phoneUser, SystemErrorCode.USER_PHONE_EXIST);
        }
        if (StringUtils.isNotBlank(dto.getEmail()) && !StringUtils.isNotBlank(dto.getEmail())){
            SysUser emailUser = sysUserMapper.findByEmail(dto.getEmail());
            Assert.isNull(emailUser, SystemErrorCode.USER_EMAIL_EXIST);
        }
        SysUser sysUser = SysUserConvert.INSTANCE.toEntity(dto);
        super.updateById(sysUser);
        //用户和岗位关联
        sysUserPostService.batchUpdate(sysUser.getId(), dto.getPostIdList());
        //用户和角色关联
        sysUserRoleService.batchUpdate(sysUser.getId(), dto.getRoleIdList());
    }

    /**
     * 删除
     * @param id id
     */
    public void delete(Long id){
        super.deleteById(id);
        //todo 需要把跟用户关联的信息都删除

    }

    public SysUser findByName(String account){
        return sysUserMapper.findByName(account);
    }

}